home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr05 / xnot12a.zip / FILEIO.C < prev    next >
C/C++ Source or Header  |  1993-06-16  |  16KB  |  773 lines

  1. #include "jam.h"
  2. /*
  3.  * Name:    Mg 2a
  4.  *         MSDOS file I/O (TurboC 1.5)
  5.  */
  6. #include <stdlib.h>
  7. #include <string.h>
  8.  
  9. #ifdef SomeUnix                /* hack, really UNIX but .... */
  10. # include "unistd.h"
  11. #endif
  12.  
  13. #ifdef FAT        /* DOS filesystem stuff */
  14. # include <io.h>
  15. # include <direct.h>
  16.   extern BOOL no_write_cr;      /* JAM */
  17. #endif
  18.  
  19. #include "def.h"
  20. #include "keyname.h"
  21. #include <stdio.h>
  22. #include "kbd.h"
  23.  
  24. static    FILE    *ffp;
  25. static BOOL doencrypt = FALSE;
  26. static void rn_(evalEnv, (char *src, char *dest));
  27.  
  28. void setencryptstate(flag)
  29. BOOL flag;
  30. {
  31. #ifdef DOCRYPT
  32.   doencrypt = flag;
  33. #endif
  34. }
  35. /*
  36.  * Open a file for reading.
  37.  */
  38. ffropen(fn)
  39. char    *fn;
  40. {
  41.  if ((ffp=fopen(fn, "rb")) == NULL)
  42.    return (FIOFNF);
  43.  return (FIOSUC);
  44. }
  45.  
  46. /*
  47.  * Open a file for writing.
  48.  * Return TRUE if all is well, and
  49.  * FALSE on error (cannot create).
  50.  */
  51. ffwopen(fn)
  52. char    *fn;
  53. {
  54.     if ((ffp=fopen(fn, "wb")) == NULL) {
  55.         ewprintf("Cannot open file for writing");
  56.         return (FIOERR);
  57.     }
  58.     return (FIOSUC);
  59. }
  60.  
  61. /*
  62.  * Close a file.
  63.  * Should look at the status.
  64.  */
  65. ffclose()
  66. {
  67.     (VOID) fclose(ffp);
  68.     return (FIOSUC);
  69. }
  70.  
  71. /*
  72.  * Write a line to the already
  73.  * opened file. The "buf" points to the
  74.  * buffer, and the "nbuf" is its length, less
  75.  * the free newline. Return the status.
  76.  * Check only at the newline.
  77.  */
  78. ffputline(buf, nbuf)
  79. register char buf[];
  80. int nbuf;
  81. {
  82.     register int i;
  83.         char c;
  84.  
  85.     for (i=0; i<nbuf; ++i)
  86.           {
  87.           c = (char)(buf[i] & 0xFF);
  88. #ifdef DOCRYPT
  89.           if (doencrypt)
  90.             docrypt(&c, 1);
  91. #endif
  92.           putc(c, ffp);
  93.           }
  94.  
  95. #ifdef FAT
  96.     if (!no_write_cr)
  97.       putc('\r', ffp);
  98. #endif
  99.     putc('\n', ffp);
  100.     if (ferror(ffp) != FALSE) {
  101.         ewprintf("Write I/O error");
  102.         return (FIOERR);
  103.     }
  104.     return (FIOSUC);
  105. }
  106.  
  107. /*
  108.  * Write a buffer to the already
  109.  * opened file. bp points to the
  110.  * buffer. Return the status.
  111.  * Check only at the newline and
  112.  * end of buffer.
  113.  */
  114. ffputbuf(bp)
  115. BUFFER *bp;
  116. {
  117.     register char *cp;
  118.     char c;
  119.     register char *cpend;
  120.     register LINE *lp;
  121.     register LINE *lpend;
  122.     register int count;
  123.  
  124.     lpend = bp->b_linep;
  125.     lp = lforw(lpend);
  126.     do {
  127.         extern BOOL clearLineChange;
  128.  
  129.         if (clearLineChange)
  130.           changelineflag(lp, FALSE);
  131.  
  132.     cp = <ext(lp)[0];        /* begining of line    */
  133.     cpend = &cp[llength(lp)];    /* end of line        */
  134.     count = 0;
  135.     while(cp != cpend) {
  136.             c = *cp++;
  137. #ifdef DOCRYPT
  138.             if (doencrypt)
  139.               docrypt(&c, 1);
  140. #endif
  141.         putc(c, ffp);
  142.         count++;
  143.     }
  144.     lp = lforw(lp);
  145.  
  146.     if(lp == lpend)        /* no implied newline on last line */ 
  147.           {
  148.             break;
  149.           }
  150.  
  151. #ifdef FAT
  152.     if (!no_write_cr)
  153.       putc('\r', ffp);
  154. #endif
  155.     putc('\n', ffp);
  156.     } while(!ferror(ffp));
  157.  
  158.     if(ferror(ffp)) {
  159.     ewprintf("Write I/O error");
  160.     return FIOERR;
  161.     }
  162.     return FIOSUC;
  163. }
  164.  
  165. /*
  166.  * Read a line from a file, and store the bytes
  167.  * in the supplied buffer. Stop on end of file or end of
  168.  * line. Don't get upset by files that don't have an end of
  169.  * line on the last line; this seem to be common on CP/M-86 and
  170.  * MS-DOS.  Delete any CR followed by a NL:  This is the normal
  171.  * format for MS_DOS files, but also occurs when files are transferred
  172.  * from VMS or MS-DOS to Unix.
  173.  */
  174. ffgetline(buf, nbuf, nbytes)
  175. register char    buf[];
  176. int nbuf;
  177. register int    *nbytes;
  178. {
  179.     register int    c;
  180.     register int    i;
  181.         char cc;
  182.  
  183.     i = 0;
  184.     for (;;) {
  185.         c = getc(ffp);
  186. rescan:
  187.         if (c == '\r') {        /* Delete any non-stray    */
  188.             c = getc(ffp);        /* carriage returns.    */
  189.             if (c != '\n') {
  190.                 buf[i++] = '\r';
  191.                 if (i >= nbuf) 
  192.                                     return FIOLONG;
  193.                 goto rescan;
  194.             }
  195.         }
  196.         if (c==EOF || c==(char)'\n')        /* End of line.    */
  197.             break;
  198.                 cc = (char)c;
  199. #ifdef DOCRYPT
  200.                 if (doencrypt)
  201.                   docrypt(&cc, 1);
  202. #endif
  203.         buf[i++] = (char)cc;
  204.         if (i >= nbuf) 
  205.               return FIOLONG;
  206.     }
  207.     if (c == EOF  && ferror(ffp) != FALSE) {
  208.         ewprintf("File read error");
  209.         return FIOERR;
  210.     }
  211.     *nbytes = i;
  212.     return c==EOF ? FIOEOF : FIOSUC;
  213. }
  214.  
  215. /*
  216.  * Rename the file "fname" into a backup copy.
  217.  * On Unix the backup has the same name as the
  218.  * original file, with a "~" on the end - unfortunately
  219.  * this does not map well to MS-DOS - the old .bak convention
  220.  * is used.
  221.  */
  222. fbackupfile(fname)
  223. char    *fname;
  224. {
  225.     register char    *nname, *ptr;
  226.     char *strchr();
  227.         char *bak = ".bak";
  228.  
  229.         /* copy the name, allocating enough space for 
  230.         * backup extension and trailing NULL
  231.         */
  232.     if ((nname=malloc(strlen(fname)+strlen(bak)+1)) == NULL)
  233.         return (ABORT);
  234.     (void) strcpy(nname, fname);
  235. #ifdef FAT
  236.     if ((ptr = strchr(nname, '.')) != 0)
  237.         strcpy(ptr, bak);
  238.     else
  239. #endif
  240.       strcat(nname, bak);
  241.     if (strcmp(fname, nname) == 0) {
  242.         free(nname);
  243.         return FALSE;
  244.     }
  245. #ifdef FAT
  246.     (void) unlink(nname);        /* Ignore errors.    */
  247.     (void) rename(fname, nname);
  248. #else /* unix land, cp to backup name to preserve softlinked 'originals' */
  249.     {
  250.       char *cmd;
  251.           static char *cpstr = "cp %s %s";
  252.  
  253.       cmd = malloc(strlen(cpstr) + strlen(fname) + strlen(nname) + 10);
  254.       sprintf(cmd, cpstr, fname, nname);
  255.       system(cmd);
  256.       free(cmd);
  257.     }
  258. #endif
  259.     free(nname);
  260.     return (TRUE);
  261. }
  262.  
  263. /*
  264.  * The string "fn" is a file name.
  265.  * convert all filenames to lower case, and convert all '\\' characters
  266.  * to forward slashes.  This is simply my preference, uppercase and
  267.  * back slashes are also viable.
  268.  */
  269. /*ARGSUSED*/
  270. adjustnamecase(fn)
  271. register char    *fn;
  272. {
  273. #ifdef FAT   /* only OS currently supported which doesn't care */
  274.   register char c;
  275.  
  276.   while ((c = *fn) != '\0') {
  277.     if (ISUPPER(c))
  278.         *fn = (char)TOLOWER(c);
  279.     if (c=='/')
  280.         *fn = BDC1;
  281.     ++fn;
  282.   }
  283. #endif
  284.   return TRUE;
  285. }
  286.  
  287.  
  288. /*
  289.  * Check first in startup directory, then local work dir, then
  290.  * check env variable (order is probably wrong...)
  291.  */
  292. char *startupfile(path) 
  293. char *path;
  294. {
  295.   register char *home = homedir;
  296.   static char name[NFILEN];
  297.   char thename[NFILEN];
  298.   register int i;
  299. #define EXT ".st"
  300.   BOOL success = FALSE;
  301.  
  302.   /* build start file name
  303.   */
  304. #ifdef WINDOWED
  305.   strcpy(thename, g_APPNAME);
  306. #else
  307.   strcpy(thename, AppName);
  308. #endif
  309.   strcat(thename, EXT);
  310.   for (i = 0; thename[i]; i++)
  311.     if (ISUPPER(thename[i]))
  312.       thename[i] = (char)TOLOWER(thename[i]);
  313.  
  314.   /* file in home dir?
  315.   */
  316.   if (!success && home && *home)
  317.     {
  318.     name[0] = '\0';
  319.     strcpy(name, home); 
  320.     i = strlen(name);
  321.  
  322.     if (i && (name[i-1] != BDC1))
  323.       name[i++] = BDC1;  
  324.     name[i] = 0;
  325.  
  326.     strcat(name, thename);
  327.     if (access(name, F_OK) == 0)
  328.       success = TRUE;
  329.     }
  330.  
  331.   /* file in startup dir?
  332.   */
  333.   if (!success && path)
  334.    {
  335.      strcpy(name, path);
  336.      strcat(name, thename);
  337.      if (access(name, F_OK) == 0)
  338.        success = TRUE;
  339.    }
  340.  
  341.   /* file in current dir?
  342.   */
  343.   if (!success && (access(thename, F_OK) == 0))
  344.     {
  345.       strcpy(name, thename);
  346.       success = TRUE;
  347.     }
  348.  
  349.   if (success)
  350.     return (name);
  351.   return NULL;
  352. }
  353.  
  354.  
  355. /*******************************************************************/
  356. /* new stuff between release 1a and 2a                             */
  357. /*******************************************************************/
  358.  
  359. /* convert all filenames to a canonical format, which in the case of
  360.  * MSDOS is X:/currentdir/filename.  Note that each drive letter has
  361.  * it's OWN current directory, so if the user specifies a drive letter,
  362.  * we use that drive's current directory, not it's root.
  363.  */
  364.  
  365. /* MSC doesn't have getdrive and getcurdir routines; simulate them. 
  366.  * They are both pretty gross.  Blame Microsoft 
  367.  *
  368.  *  (code moved to dos.c - JAM) 
  369.  */
  370.  
  371. /* This DOS version of this code already does the right thing
  372. * for constructing full pathnames from partial ones; I need to
  373. * do special ughly stuff for UN*X systems, especially since I
  374. * add incremental saves and path preloading... JAM
  375. *
  376. * Note that this code does not check for overflow!
  377. */ 
  378. char *adjustname(fn)
  379. register char *fn;
  380. {
  381.     register char *cp;
  382. #ifdef FAT
  383.     static char fnb[NFILEN * 2];
  384.  
  385.     cp = fnb;
  386.     /* handle A:foo\bar */
  387.     if (fn[0] && fn[1] == ':') {
  388.     *cp++ = *fn++;
  389.     *cp++ = *fn++;
  390.     *cp = '\0';
  391.     adjustnamecase(fnb);    /* force case to lower */
  392.  
  393.     if (*fn != '/' && *fn != BDC1) {
  394.         *cp++ = BDC1;
  395.         getcurdir((unsigned short)(fnb[0]-'a'+1), cp);
  396.         cp = fnb + strlen(fnb);
  397.     }
  398.     else
  399.         *cp++ = *fn++;
  400.     }
  401.     /* handle \foo\bar */
  402.     else if (*fn == '/' || *fn == BDC1) {
  403.     *cp++ = (char) (getdisk() + 'a');
  404.     *cp++ = ':';
  405.     *cp++ = BDC1; fn++;
  406.     }
  407.     else {
  408.     strcpy(fnb, wdir);
  409.     cp = fnb + strlen(fnb);
  410.     }
  411.  
  412.     if(cp != fnb && cp[-1] != '/' && cp[-1] != BDC1) 
  413.     *cp++ = BDC1;
  414.  
  415.     /* at this point, we should have a drive, and at least a single */
  416.     /* slash.  Now copy over the rest of the filename, while handling */
  417.     /* certain pathalogical cases */
  418.  
  419.     /* convert "//" to "/", "/./" to "/", and "/x/../" to "/" */
  420.     while(*fn) {
  421.         switch(*fn) {
  422.         case '.':
  423.         switch(fn[1]) {
  424.                 case '\0':
  425.                 *--cp = '\0';
  426.             adjustnamecase(fnb);
  427.                 return fnb;
  428.             case BDC1:
  429.                     fn += 2;
  430.                 continue;
  431.             case '.':
  432.                 if(fn[2]=='/' || fn[2]==BDC1 || fn[2] == '\0') {
  433.                 --cp;
  434.                 while(cp > fnb && *--cp != '/' && *cp != BDC1)
  435.                 ;
  436.                 if (cp==fnb) cp += 2;
  437.                 ++cp;
  438.                 if(fn[2]=='\0') {
  439.                     *--cp = '\0';
  440.                 adjustnamecase(fnb);
  441.                     return fnb;
  442.                 }
  443.                     fn += 3;
  444.                     continue;
  445.                 }
  446.                 break;
  447.             default:
  448.                 break;
  449.             }
  450.         break;
  451.         case '/':
  452.         case BDC1:
  453.             fn++;
  454.             continue;
  455.         default:
  456.             break;
  457.     }
  458.     while(*fn && (*cp++ = *fn++) != '/' && fn[-1] != BDC1)
  459.         ;
  460.     }
  461.     if (cp != fnb + 3 && cp[-1]==BDC1) 
  462.       --cp;
  463.     *cp = '\0';
  464. #else                    /* assuming UN*X here... */
  465.     static char fnb[NFILEN * 2];
  466.  
  467.     if ((fn[0] == '~') && homedir && *homedir)
  468.       {
  469.         strcpy(fnb, homedir);
  470.         strcat(fnb, &fn[1]);        /* assuming ~/foo so / already there */
  471.       }
  472.     else if (fn[0] == '$')              /* env variable */
  473.       evalEnv(fn, fnb);
  474.     else if (fn[0] == BDC1)             /* full path assumed! */ 
  475.       strcpy(fnb, fn);
  476.     else
  477.       {
  478.       BUFFER temp;
  479.       char buf[NFILEN];
  480.       int i;
  481.  
  482.       memset(&temp, 0, sizeof(BUFFER));
  483.       strcpy(temp.b_fname, fn);       /* use dir code to expand path */
  484.       if (switchdir(&temp))             /* if ok, use it else punt */
  485.         {
  486.           strcpy(fnb, dirpath());
  487.           i = strlen(fn) - 1;
  488.           while (i >= 0)
  489.             if (fn[i] == BDC1)        /* end of name */
  490.               {
  491.                 i++;
  492.                 break;
  493.               }
  494.             else
  495.               i--;
  496.  
  497.           if (i < 0)
  498.             i = 0;
  499.           strcat(fnb, &fn[i]); 
  500.         }
  501.       else
  502.         strcpy(fnb, fn);
  503.       }
  504. #endif /* FAT */
  505.     adjustnamecase(fnb);
  506.     return fnb;
  507. }
  508.  
  509. /* I completely re-hacked this code; it used to
  510. * spawn command.com to get the names. Now, it calls
  511. * an OS specific function which is in dos.c or unix.c.
  512. * Note that the unix version does a system call, but it's
  513. * much cleaner that this was, portable and the dos
  514. * version works on DOS, Windows, and NT!  (JAM)
  515. */
  516. BUFFER *dired_(dirname)
  517. char *dirname;
  518. {
  519.     register BUFFER *bp;
  520.     int i;
  521.     register char *s;
  522.  
  523.     if((dirname = adjustname(dirname)) == NULL) {
  524.     ewprintf("Bad directory name");
  525.     return NULL;
  526.     }
  527.       
  528.       adjustnamecase(dirname);
  529.       s = malloc(strlen(dirname) + 4);
  530.       strcpy(s, dirname);
  531.       i = strlen(s) - 1;
  532.       if (s[i] != BDC1)
  533.     {
  534.           i++;
  535.       s[i++] = BDC1;
  536.       s[i] = '\0';
  537.     }
  538.  
  539.      if (!(bp = bfind(s, TRUE)))
  540.         {
  541.       ewprintf("Could not create buffer");
  542.           free(s);
  543.       return NULL;
  544.         }
  545.      if(bclear(bp) != TRUE) 
  546.         {
  547.           free(s);
  548.       return FALSE;
  549.     }
  550.  
  551.      GetDiskDirectory(bp, s);
  552.  
  553.     bp->b_dotp = lforw(bp->b_linep);        /* go to first line */
  554.     (VOID) strncpy(bp->b_fname, dirname, NFILEN);
  555.     if((bp->b_modes[0] = name_mode(DiredStr)) == NULL) {
  556.     bp->b_modes[0] = &map_table[0];
  557.     ewprintf(modeerr, DiredStr);
  558.         free(s);
  559.     return NULL;
  560.     }
  561.     bp->b_flag |= BFVIEW;
  562.     bp->b_flag &= ~BFREVERT;
  563.     bp->b_nmodes = 0;
  564.     free(s);
  565.     return bp;
  566. }
  567.  
  568. /* This is special hack code to do my version of
  569. * file name auto-complete. (JAM)
  570. */
  571. int diredfiles_(f, n)
  572. int f, n;
  573. {
  574.     register BUFFER *bp;
  575.     char buffer[NFILEN * 4]; 
  576.     char bname[NFILEN], dspec[NFILEN * 4];
  577.     int i;
  578.     BOOL found = FALSE;
  579.  
  580.     getcmdinput(buffer);      
  581.     adjustnamecase(buffer);
  582. #ifdef SomeUnix
  583.     {
  584.       char buf1[NFILEN * 4];
  585.       evalEnv(buffer, buf1);
  586.       strcpy(buffer, buf1);
  587.     }
  588. #endif
  589.     strcpy(bname, buffer);
  590.  
  591.     for (i = 0; i < (int)strlen(bname); i++)
  592.       if (bname[i] == '*')
  593.         {
  594.           found = TRUE;
  595.           break;
  596.         }
  597.     if (!found)
  598. #ifdef SomeUnix
  599.       strcat(bname, "*");
  600. #else
  601.       strcat(bname, "*.*");
  602. #endif
  603.     strcpy(dspec, buffer);
  604.  
  605.     /* fixup buffer now so is real dir name
  606.     */
  607.     for (i = strlen(buffer)-1; i >= 0; i--)
  608.       {
  609.       if (buffer[i] == BDC1)
  610.         break;
  611.       buffer[i] = '\0';
  612.       }
  613.     
  614.     /* make the buffer
  615.     */
  616.     if (!(bp = bfind(bname, TRUE)))
  617.         {
  618.       ewprintf("Could not create buffer");
  619.       return FALSE;
  620.         }
  621.     if(bclear(bp) != TRUE) 
  622.     return FALSE;
  623.     GetDiskDirectory(bp, dspec);
  624.  
  625.     bp->b_dotp = lforw(bp->b_linep);        /* go to first line */
  626.     (VOID) strncpy(bp->b_fname, buffer, NFILEN);
  627.     if((bp->b_modes[0] = name_mode(DiredStr)) == NULL) {
  628.     bp->b_modes[0] = &map_table[0];
  629.     ewprintf(modeerr, DiredStr);
  630.     return FALSE;
  631.     }
  632.     bp->b_nmodes = 0;
  633.     bp->b_flag |= BFVIEW;
  634.     return (popbuf(bp) ? TRUE : FALSE);
  635. }
  636.  
  637. /* This is really ugly, but then so was the UNIX version! 
  638. */
  639. /* This version knows that the dir buffer was built
  640. * via DOS/UN*X calls and word (foo.xxx) is the file name,
  641. * ie it is better than before because at least now line formatting
  642. * is controled OS-independent.   - JAM
  643. */
  644. d_makename(lp, fn)
  645. LINE *lp;
  646. char *fn;
  647. {
  648.  register int i, j;
  649.  char c;
  650.  
  651.  /* buffer name not useful for filename 
  652.  * if not regular dir-built buffer
  653.  */
  654.  if ((curbp->b_flag & BFREVERT) == 0)
  655.    {
  656.      (VOID) strcpy(fn, curbp->b_fname);
  657.  
  658.      /* scrub the wildcard which may be here from diredfiles_
  659.      */
  660.      for (i = 0; fn[i]; i++)
  661.        if (fn[i] == '*')
  662.          {
  663.            fn[i] = '\0';     /* should only be this one */
  664.            break;
  665.          }
  666.    }
  667.         
  668.  else
  669.    fn[0] = '\0';
  670.  
  671.  j = strlen(fn);
  672.  if (j > 0)
  673.    if (fn[j-1] != BDC1)
  674.      fn[j++] = BDC1;
  675.  
  676.  /* find the word; if on space/tab, error and return\
  677.  * else backup to word and then copy till work end
  678.  */
  679.  if (((c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ') ||
  680.        ( c == '\t'))
  681.    {
  682.      ewprintf("Cursor not on a filename.");
  683.      return(ABORT);
  684.    }
  685.  
  686.  /* backup to whitespace
  687.  */
  688.  for (i = curwp->w_doto; i >= 0; i--)
  689.   {
  690.     c = lgetc(lp, i);
  691.     if ((c == ' ') || (c == '\t'))
  692.       {
  693.         i++;
  694.         break;
  695.       }
  696.   }
  697.  
  698.  if ( i < 0)
  699.    i = 0;
  700.  
  701.  for (; i < llength(lp); i++, j++)
  702.   {
  703.   fn[j] = lgetc(lp, i);
  704.   if ((fn[j] == ' ') || (fn[j] == '\t'))
  705.     break;
  706.   }
  707.  fn[j] = '\0';
  708.  
  709.  /*lowercase this
  710.  */
  711.  adjustnamecase(fn);
  712.  return(0);              /* DosGetDirectory returns NORMAL files only */
  713. }
  714.  
  715. /* For some reason, this was a system call!
  716. */
  717. #if 0
  718. copy(frname, toname)
  719. char *frname, *toname;
  720. {
  721.    FILE  *in, *out;
  722.    char c = ~EOF;
  723.  
  724.    in = fopen(frname, "rb");
  725.    out = fopen(toname, "wb");
  726.    
  727.    if (in && out)
  728.      {
  729.        for (;c != EOF;)
  730.         if ((c = (char)getc(in)) != EOF)
  731.         putc(c, out);
  732.        fclose(in);
  733.        fclose(out);
  734.      }    
  735.  
  736.     return TRUE;
  737. }
  738. #endif
  739.  
  740. BOOL fileisok(s)
  741. char *s;
  742. {
  743.   return(access(s, F_OK) == 0); 
  744. }
  745.  
  746. #ifdef SomeUnix         /* someday I might make this work for DOS! */
  747. static void evalEnv(s, d)
  748. char *s, *d;
  749. {
  750.   char envvar[NFILEN], *p;
  751.   int i;
  752.  
  753.   /* To get here means s[0] == $ so expand it out and
  754.   * construct a useful name
  755.   *
  756.   * Collect env name up to standard delim (BDC1) or
  757.   * EOF on string
  758.   */
  759.   for (i = 1; s[i] && (s[i] != BDC1); i++)
  760.     envvar[i-1] = s[i];
  761.   envvar[i-1] = 0;
  762.  
  763.   p = (char *)getenv(envvar);
  764.   if (p && *p)
  765.     {
  766.       strcpy(d, p);    
  767.       strcat(d, &s[i]);
  768.     }
  769.   else
  770.     strcpy(d, s);               /* failure defaults to original */
  771. }
  772. #endif
  773.